package controllers

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/base64"
	"encoding/xml"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/url"
	"reflect"
	"regexp"
	"sort"
	"strconv"
	"strings"
	"text/template"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/cache"
	SunnyUtil "github.com/sunnyregion/util"
	"github.com/sunnyregion/util/sunnyjson"
	"github.com/traefik/yaegi/interp"
	"github.com/traefik/yaegi/stdlib"
)

var left_placeholder = "["
var right_placeholder = "]"
var reg_str = `\` + left_placeholder + `.*?\` + right_placeholder + ``
var regMaps = map[string]interface{}{
	`{{range.*?\}}`: TemplateAddCount,
}

// @Description 将tpl替换为data的相关数据
// @Param	tpl:要替换的字符串
// @Param	data:替换内容
// @return newTpl: 替换后的字符串； newData：删除替换内容后的对象
func ParseTpl(tpl string, data map[string]interface{}) (newTpl string, newData map[string]interface{}) {
	if len(tpl) < len(left_placeholder)+len(right_placeholder)+1 {
		return tpl, data
	}
	expressions := getTplExpressions(tpl)
	flatData := MapFlat("", data)
	for _, exp := range expressions {
		exp = strings.TrimSpace(exp)
		tpl = strings.Replace(tpl, left_placeholder+exp+right_placeholder, ToString(flatData[exp]), -1)
		delete(data, exp)
	}
	return tpl, data
}

//将map[string]interface{}变为一级结构
func MapFlat(prefix string, mapData map[string]interface{}) map[string]interface{} {
	v := reflect.ValueOf(mapData)
	res := make(map[string]interface{})
	foreachObj(prefix, v, res)
	return res
}

func combinePath(pre, path string) string {
	if pre != "" && path != "" {
		return pre + "." + path
	}
	return pre + path
}

func foreachObj(pre string, v reflect.Value, res map[string]interface{}) {
	switch v.Kind() {
	case reflect.Ptr:
		foreachObj(pre, v.Elem(), res)
	case reflect.Array, reflect.Slice:
		for i := 0; i < v.Len(); i++ {
			foreachObj(combinePath(pre, strconv.Itoa(i)), v.Index(i), res)
		}
	case reflect.Struct:
		vType := v.Type()
		for i := 0; i < v.NumField(); i++ {
			foreachObj(combinePath(pre, vType.Field(i).Name), v.Field(i), res)
		}
	case reflect.Map:
		for _, key := range v.MapKeys() {
			foreachObj(combinePath(pre, key.String()), v.MapIndex(key), res)
		}
	case reflect.Interface:
		foreachObj(combinePath(pre, ""), v.Elem(), res)
	default:
		res[pre] = v.Interface()
	}
}

// 得到tpl替换词数组
func getTplExpressions(str string) []string {
	reg_str := `\` + left_placeholder + `.*?\` + right_placeholder + ``
	re, _ := regexp.Compile(reg_str)
	all := re.FindAll([]byte(str), -1)
	keyArrays := make([]string, 0)
	for _, item := range all {
		item_str := string(item)
		if len(item_str) > (len(left_placeholder) + len(right_placeholder)) {
			item_str = item_str[len(left_placeholder) : len(item_str)-len(right_placeholder)]
			keyArrays = append(keyArrays, item_str)
		}
	}
	return keyArrays

}

//得到缓存key
func GetHashKey(interfaceId, params string) (key string, err error) {
	var argu map[string]interface{}
	if params != "" {
		err = sunnyjson.Unmarshal([]byte(params), &argu)
		if err != nil {
			return
		}
	}

	flatArgu := MapFlat("", argu)
	var buffer bytes.Buffer
	buffer.WriteString(interfaceId)
	keys := []string{}
	for key, _ := range flatArgu {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	for _, v := range keys {
		vNew := ToString(flatArgu[v])
		vStr := v + vNew
		if len(vNew) > 100 {
			vStr = v + vNew[:100]
		}
		buffer.WriteString(vStr)
	}
	key = buffer.String()
	return key, err
}

//获取memcache信息
// @Param key string    true 缓存key
// @Param argu map[string]string   true 接口参数
// @Return b, istrue   []byte, bool   true 返回数据，是否存在
func GetMemcache(key string) ([]byte, bool) {
	ip := `{"conn":"` + beego.AppConfig.String("memcache::host") + ":" + beego.AppConfig.String("memcache::port") + `"}`
	bm, _ := cache.NewCache("memcache", ip)

	var sms []byte
	istrue := bm.IsExist(key)
	if !istrue {
		return sms, istrue
	}
	b := bm.Get(key).([]byte)
	return b, istrue
}

//存memcache信息
// @Param key string    true 缓存key
// @Param argu map[string]string   true 接口参数
// @Return result,err  []byte, error   true 返回数据额和错误信息
func PutMemcache(key string, sms []byte, mtime int) error {
	ip := `{"conn":"` + beego.AppConfig.String("memcache::host") + ":" + beego.AppConfig.String("memcache::port") + `"}`
	bm, _ := cache.NewCache("memcache", ip)
	err := bm.Put(key, string(sms), time.Duration(mtime)*time.Second)
	return err
}

//删除memcache信息
// @Param key string    true 缓存key
// @Return result,err     true 返回数据额和错误信息
func DelMemcache(key string) error {
	ip := `{"conn":"` + beego.AppConfig.String("memcache::host") + ":" + beego.AppConfig.String("memcache::port") + `"}`
	bm, _ := cache.NewCache("memcache", ip)
	err := bm.Delete(key)
	return err
}

// 处理byte[]数据
// @Param data   []byte
// @Return returnData,err   map[string]interface{}, error
func DealData(data []byte) (returnData interface{}, err error) {
	s := string(data[:])
	if s != "" {
		var replacer = strings.NewReplacer("\n", "", "\r", "")
		s = replacer.Replace(s)
		data = []byte(s)
		if strings.HasPrefix(s, "<?xml version=") {
			var Xml string
			err = xml.Unmarshal(data, &Xml)
			if err != nil {
				return nil, err
			}
			var replacer = strings.NewReplacer("\n", "", "\r", "", "\t", "")
			Xml = replacer.Replace(Xml)
			data = []byte(Xml)
		}
		var m interface{}
		err = sunnyjson.Unmarshal(data, &m)
		if err != nil {
			return nil, err
		} else {
			returnData = m
		}

	}
	return
}

//获取配置文件中的缓存时间
// @Param str      string    true 配置文件中名称
// @Return mtime   int       true  时间
func ConfInt(str string) int {
	api := "Api::" + str
	time := beego.AppConfig.String(api)
	mtime, _ := strconv.Atoi(time)
	return mtime
}

// 生成request
// @Param dataParams      map[string]string
// @Return http.Request
func GenerateRequest(dataParams map[string]interface{}) (request *http.Request, err error) {
	var argu map[string]interface{}
	var staticP map[string]interface{} //固定参数
	apiUrl := ToString(dataParams["apiurl"])
	apiUrl = strings.Trim(apiUrl, " ")
	headers := dataParams["headers"]
	method := dataParams["method"]
	argu = dataParams["params"].(map[string]interface{})
	staticParams := ToString(dataParams["staticparams"])
	paramType := dataParams["paramType"]
	if strings.Trim(staticParams, " ") != "" {
		err = sunnyjson.Unmarshal([]byte(strings.Trim(staticParams, " ")), &staticP)
		if err != nil {
			return nil, err
		}
	}
	if method == "get" {
		//判断有没有url替换
		regexc := regexp.MustCompile(`[[](.*?)[]]`)
		allSub := regexc.FindAllSubmatch([]byte(apiUrl), -1)
		urlParamLen := len(allSub)
		if urlParamLen > 0 {
			apiUrl, argu = ParseTpl(apiUrl, argu)
		}
		u, _ := url.Parse(apiUrl)
		q := u.Query()
		for k, v := range argu {
			q.Set(k, ToString(v))
		}
		for k, v := range staticP {
			q.Set(k, ToString(v))
		}
		u.RawQuery = q.Encode()
		beego.Info(u.String())
		request, err = http.NewRequest("GET", u.String(), nil)
	} else if method == "post" {
		if paramType == "file" {
			if argu["file"] == nil {
				return nil, errors.New("no file in params")
			}
			fileInfos := strings.Split(argu["file"].(string), ",")
			if len(fileInfos) != 2 {
				return nil, errors.New("not right base64 string")
			}
			fileTypes := fileInfos[0]
			fileType := strings.Split(strings.Split(fileTypes, ";")[0], "/")[1]
			fileStr := fileInfos[1]
			fileBytes, _ := base64.StdEncoding.DecodeString(fileStr) //成图片文件并把文件写入到buffer
			fileBuffer := bytes.NewBuffer(fileBytes)

			var b bytes.Buffer
			w := multipart.NewWriter(&b)
			fw, err := w.CreateFormFile("file", time.Now().Format("150405")+"."+fileType)
			if err != nil {
				return nil, err
			}
			if _, err = io.Copy(fw, fileBuffer); err != nil {
				return nil, err
			}
			w.Close()
			request, err = http.NewRequest("POST", apiUrl, &b)
			if err != nil {
				return nil, err
			}
			request.Header.Set("Content-Type", w.FormDataContentType())
			// Submit the request
			ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(3*time.Second))
			defer cancel()
			request.WithContext(ctx)

		} else {
			for k, v := range staticP {
				argu[k] = v
			}
			paramsStr, _ := sunnyjson.Marshal(argu)
			reader := bytes.NewReader([]byte(paramsStr))
			request, err = http.NewRequest("POST", apiUrl, reader)
		}
	}
	if err != nil {
		return nil, err
	}
	if headers != nil {
		if headersObj, ok := headers.(map[string]interface{}); ok {
			for k, v := range headersObj {
				request.Header.Set(k, ToString(v))
			}
		}
	}
	return
}

// GetSelfIFPing ...
// @Title getUrlFilePost
// @Description 人脸信息获取token
// @Param	f	multipart.File	'文件流'
// @Param	apiUrl	string	'url地址'
// @Param	filename	string	'图片名称'
// @Return result []byte 'interface status'
// @Return err error 'errors'
func getUrlFilePost(f multipart.File, apiUrl string, filename string) (result []byte, err error) {

	var b bytes.Buffer
	w := multipart.NewWriter(&b)
	fw, err := w.CreateFormFile("file", filename)
	if err != nil {
		return nil, err
	}
	if _, err = io.Copy(fw, f); err != nil {
		return nil, err
	}
	w.Close()
	req, err := http.NewRequest("POST", apiUrl, &b)
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	// Submit the request
	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(3*time.Second))
	defer cancel()
	req.WithContext(ctx)
	client := &http.Client{}
	res, err := client.Do(req)
	defer res.Body.Close()
	if err != nil {
		return nil, err
	}
	result, err = ioutil.ReadAll(res.Body)
	resCode := res.StatusCode
	if resCode != 200 {
		result = nil
		err = errors.New("服务器返回状态" + strconv.Itoa(resCode))
	}
	return result, err

}

// 生成request,并返回请求结果
// @Param params      map[string]string
// @Return data []byte
func GetInterfaceData(params map[string]interface{}) (data []byte, err error) {
	tr := &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
	client := http.Client{Transport: tr, Timeout: 3 * time.Second}
	request, err := GenerateRequest(params)
	if err != nil {
		return
	}
	resp, err := client.Do(request)
	if err != nil {
		return
	}
	defer resp.Body.Close()
	if resp.StatusCode == 200 {
		data, err = ioutil.ReadAll(resp.Body)
	} else {
		err = errors.New(resp.Status)
	}
	return
}

// 模板计数
func counter() func() int {
	i := -1
	return func() int {
		i++
		return i
	}
}

//解析模板对照组
//input "admin_area:area,parent_city:city"
//return  map[string]string{"admin_area":"area","parent_city":"city"}
func analyzeMapstr(mstr string) map[string]string {
	pmap := make(map[string]string)
	plist := strings.Split(mstr, ",")
	for j := 0; j < len(plist); j++ {
		kv := strings.Split(plist[j], ":")
		if len(kv) == 2 {
			pmap[kv[1]] = kv[0]
		}
	}
	return pmap
}

//模板循环列表
func rangelist(tplist []interface{}, fieldstr string) (tpl string, err error) {
	var tl []interface{}
	for i := 0; i < len(tplist); i++ {
		switch tplist[i].(type) {
		case map[string]interface{}:
			l, ok := tplist[i].(map[string]interface{})
			if ok {
				m := make(map[string]interface{})
				if fieldstr == "" {
					for k, v := range l {
						m[k] = v
					}
				} else {
					pmap := analyzeMapstr(fieldstr)
					for k, v := range pmap {
						if v == "nil" {
							m[k] = nil
						} else if f, ok := l[v]; ok {
							m[k] = f
						} else {
							ks := strings.Split(v, "|")
							for ksi := 0; ksi < len(ks); ksi++ {
								if f, ok := l[ks[ksi]]; ok {
									if f == nil || strings.Trim(f.(string), " ") == "" {
										if ksi == len(ks)-1 {
											m[k] = nil
											break
										}
										continue
									} else {
										m[k] = f
										break
									}
								}
							}
						}
					}
				}
				tl = append(tl, m)
			}
		case interface{}:
			l, ok := tplist[i].(interface{})
			if ok {
				tl = append(tl, l)
			}
		}

	}
	o, err := sunnyjson.Marshal(tl)
	if err != nil {
		return "", err
	}
	return string(o), nil
}

//模板循环map
func rangemap(maptpl map[string]interface{}, mstr string) (tpl string, err error) {
	m := make(map[string]interface{})
	//如果没有定义对照组
	if strings.Trim(mstr, " ") == "" {
		tplByte, err := sunnyjson.Marshal(maptpl)
		if err != nil {
			return "", err
		}
		return string(tplByte), nil
	} else {
		if mstr == "" {
			for k, v := range maptpl {
				m[k] = v
			}
		} else {
			pmap := analyzeMapstr(mstr)
			for k, v := range pmap {
				if v == "nil" {
					m[k] = nil
				} else if f, ok := maptpl[v]; ok {
					m[k] = f
				} else {
					ks := strings.Split(v, "|")
					for ksi := 0; ksi < len(ks); ksi++ {
						if f, ok := maptpl[ks[ksi]]; ok {
							if f == nil || strings.Trim(f.(string), " ") == "" {
								if ksi == len(ks)-1 {
									m[k] = nil
									break
								}
								continue
							} else {
								m[k] = f
								break
							}
						}
					}
				}
			}
		}
	}
	o, err := sunnyjson.Marshal(m)
	if err != nil {
		return "", err
	}
	return string(o), nil
}

// 为模板添加计数功能等，可扩展
// @Param tmp string
// @Return s string
func TemplateAddFunc(tmp string) (s string) {
	//regs := []string{`{{range.*?\}}`}
	for reg, fn := range regMaps {
		re, _ := regexp.Compile(reg)
		all := re.FindAllIndex([]byte(tmp), -1)
		funcs := reflect.ValueOf(fn)
		callValue := funcs.Call([]reflect.Value{reflect.ValueOf(all), reflect.ValueOf(tmp)})
		if err, ok := callValue[0].Interface().(error); ok {
			if nil != err {
				fmt.Println(err)
			}
		} else {
			s = callValue[0].Interface().(string)
		}
	}
	return
}
func TemplateAddCount(all [][]int, tmp string) (s string) {
	if all == nil {
		s = tmp
	} else {
		start := 0
		for _, v := range all {
			sub := strings.Replace(tmp[start:v[1]], tmp[v[0]:v[1]], `{{$c := counter}}`+tmp[v[0]:v[1]]+`{{if call $c}},{{end}}`, -1)
			s = s + sub
			start = v[1]
		}
		s = s + tmp[start:]
	}
	return
}

// 模板处理
// @Param outstruct string
// @Param returnData interface{}
// @Return templateStr string, err error
func TemplateDeal(outstruct string, returnData interface{}) (templateStr string, err error) {
	var doc bytes.Buffer
	s := TemplateAddFunc(outstruct)
	tmpl := template.New("temp")
	tmpl = tmpl.Funcs(template.FuncMap{
		"counter":   counter,
		"rangelist": rangelist,
		"rangemap":  rangemap,
	})
	tmpl, err = tmpl.Parse(s)
	if err != nil {
		return "", err
	}
	err = tmpl.Execute(&doc, returnData)
	if err != nil {
		return "", err
	}
	res := doc.String()
	var replacer = strings.NewReplacer("\n", "", "\r", "")
	templateStr = replacer.Replace(res)
	return
}

//根据参照的参数，生成参数
func getMapedParams(p_example, p_map map[string]interface{}) (paramArgu map[string]interface{}, err error) {
	paramArgu = make(map[string]interface{})
	for ke, ve := range p_example {
		for km, vm := range p_map {
			if reflect.TypeOf(vm).String() == "map[string]interface {}" {
				if ke == km {
					paramArgu[km], err = getMapedParams(ve.(map[string]interface{}), vm.(map[string]interface{}))
					delete(p_map, km)
					delete(p_example, ke)
					break
				}
			} else {
				if vm == ke {
					paramArgu[km] = ve
					delete(p_map, km)
					delete(p_example, ke)
					break
				}
			}
		}
	}
	if len(p_example) > 0 {
		var noAvaliable string
		for k, _ := range p_example {
			noAvaliable = SunnyUtil.SunnyStrJoin(noAvaliable, k, "\n")
		}
		err = errors.New("\nSub params are not avaliable:\n" + noAvaliable)
		return nil, err
	}
	return paramArgu, nil
}

//从string对象中，找到某个值
func GetFromStrObj(k, src string) string {
	var p map[string]string
	err := sunnyjson.Unmarshal([]byte(src), &p)
	if err != nil {
		return ""
	}
	if v, ok := p[k]; ok {
		return v
	}
	return ""
}

// 执行嵌入函数
// @Param paramsBody post body
// @Param paramsExample params example
// @Param staticParams static params
// @Param evalSrc eval string
// @Return evalRes string, err error
func GetEvalParams(paramsBody, paramsExample, staticParams, evalSrc string) (evalRes map[string]interface{}, err error) {
	var bodyParams map[string]interface{}
	if strings.Trim(paramsBody, " ") != "" {
		err = sunnyjson.Unmarshal([]byte(paramsBody), &bodyParams)
		if err != nil {
			return nil, err
		}
	}

	var pInterface map[string]interface{}
	if strings.Trim(paramsExample, " ") != "" {
		err = sunnyjson.Unmarshal([]byte(paramsExample), &pInterface)
		if err != nil {
			return nil, err
		}
	}
	staticParams = strings.Trim(staticParams, " ")
	params, ok := pInterface["params"]
	if !ok {
		params = pInterface
	}
	returnObj := make(map[string]interface{})
	var interParams []string
	if params != nil {
		if pParams, ok := params.([]interface{}); ok {
			for k, v := range pParams {
				var subParamStr string
				switch v.(type) {
				case map[string]interface{}:
					if subParam, ok := v.(map[string]interface{}); ok {
						for subk, subv := range subParam {
							subvStr := ToString(subv)
							if strings.HasPrefix(subvStr, "$static_") {
								subParam[subk] = GetFromStrObj(subvStr[8:], staticParams)
							} else if strings.HasPrefix(subvStr, "$fixed_") {
								subParam[subk] = subvStr[7:]
							} else if bodyParams != nil {
								existV, ok := bodyParams[subk]
								if ok {
									subParam[subk] = existV
									delete(bodyParams, subk)
								} else {
									err = errors.New(subk + " is needed")
									return nil, err
								}
							}
						}
						subParamByte, _ := sunnyjson.Marshal(subParam)
						subParamStr = string(subParamByte)
					}
				case string, int:
					vstr := ToString(v)
					if strings.HasPrefix(vstr, "$static_") {
						pParams[k] = GetFromStrObj(vstr[8:], staticParams)
						subParamStr = pParams[k].(string)
						returnObj[vstr[8:]] = pParams[k]
					} else if strings.HasPrefix(vstr, "$fixed_") {
						pParams[k] = vstr[7:]
						subParamStr = vstr[7:]
					}
				}
				interParams = append(interParams, subParamStr)
			}
		}
	}
	if len(bodyParams) != 0 {
		var s []string
		for k := range bodyParams {
			s = append(s, k)
		}
		err = errors.New("params " + strings.Join(s, ",") + " is not needed")
		return nil, err
	}
	i := interp.New(interp.Options{})
	i.Use(stdlib.Symbols)
	evaltxt, err := ioutil.ReadFile("conf/intereval/intereval")
	if err != nil {
		return nil, err
	}
	_, err = i.Eval(string(evaltxt))
	if err != nil {
		return nil, err
	}
	evalSrc = "package intereval;" + evalSrc
	_, err = i.Eval(evalSrc)
	if err != nil {
		return nil, err
	}
	b, err := i.Eval("intereval.doWork")
	evalfn := b.Interface().(func([]string) map[string]interface{})
	evalRes = evalfn(interParams)
	return
}

// 将接口结果合并
// @Param firstresult string 第一个结果
// @Param secondresult string 第二个结果
// @Return mergedres string 合并后的结果，按第二个结果返回， err error
func MergeResults(firstresult, secondresult string) (mergedres string, err error) {
	var firstres interface{}
	var secondres interface{}
	err = sunnyjson.Unmarshal([]byte(firstresult), &firstres)
	if err != nil {
		return
	}
	err = sunnyjson.Unmarshal([]byte(secondresult), &secondres)
	if err != nil {
		return
	}

	if f, ok := firstres.(map[string]interface{}); ok {
		if s, ok := secondres.(map[string]interface{}); ok {
			for k, v := range s {
				switch v.(type) {
				case string:
					if nv, ok := f[v.(string)]; ok {
						s[k] = nv
					}
				}
			}
			res, _ := sunnyjson.Marshal(s)
			mergedres = string(res)
		} else {
			mergedres = secondresult
		}
	} else {
		mergedres = secondresult
	}
	return
}
